package com.zusmart.core.context.support;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import com.zusmart.base.activity.support.AbstractActivity;
import com.zusmart.base.logging.Logger;
import com.zusmart.base.logging.LoggerFactory;
import com.zusmart.base.util.GenericUtils;
import com.zusmart.base.util.StringUtils;
import com.zusmart.core.bean.BeanDefinition;
import com.zusmart.core.bean.BeanDefinitionRegistry;
import com.zusmart.core.bean.cause.BeanDefinitionMultipleException;
import com.zusmart.core.bean.cause.BeanDefinitionNotFoundException;
import com.zusmart.core.bean.cause.BeanInstanceNotFoundException;
import com.zusmart.core.context.ApplicationContext;

public abstract class AbstractApplicationContext extends AbstractActivity<com.zusmart.core.context.ApplicationContext> implements ApplicationContext {

	private static final Logger logger = LoggerFactory.getLogger(AbstractApplicationContext.class);

	protected BeanDefinitionRegistry beanDefinitionRegistry;

	protected AbstractApplicationContext(String[] args) {
		this.resolveArguments(args);
		this.beanDefinitionRegistry = this.createBeanDefinitionRegistry();
	}

	@Override
	public <T> T getBean(Class<T> beanType, String beanName) {
		if (null == beanType && StringUtils.isBlank(beanName)) {
			return null;
		}
		if (null == beanType && StringUtils.isNotBlank(beanName)) {
			return this.getBean(beanName);
		} else if (null != beanType && StringUtils.isBlank(beanName)) {
			return this.getBean(beanType);
		} else {
			try {
				BeanDefinition beanDefinition = this.beanDefinitionRegistry.getBeanDefinition(beanName, beanType);
				return GenericUtils.parse(beanDefinition.getBeanInstance());
			} catch (BeanDefinitionNotFoundException e) {
				logger.warn("bean definition not found {}#{}", beanType, beanName);
			} catch (BeanDefinitionMultipleException e) {
				logger.warn("bean definition multiple {}#{}", beanType, beanName);
			} catch (BeanInstanceNotFoundException e) {
				logger.warn("bean instance not found {}#{}", beanType, beanName);
			}
			return null;
		}
	}

	@Override
	public <T> T getBean(Class<T> beanType) {
		if (null == beanType) {
			return null;
		}
		try {
			BeanDefinition beanDefinition = this.beanDefinitionRegistry.getBeanDefinition(beanType);
			return GenericUtils.parse(beanDefinition.getBeanInstance());
		} catch (BeanDefinitionNotFoundException e) {
			logger.warn("bean definition not found {}", beanType);
		} catch (BeanDefinitionMultipleException e) {
			logger.warn("bean definition multiple {}", beanType);
		} catch (BeanInstanceNotFoundException e) {
			logger.warn("bean instance not found {}", beanType);
		}
		return null;
	}

	@Override
	public <T> T getBean(String beanName) {
		if (StringUtils.isBlank(beanName)) {
			return null;
		}
		try {
			BeanDefinition beanDefinition = this.beanDefinitionRegistry.getBeanDefinition(beanName);
			return GenericUtils.parse(beanDefinition.getBeanInstance());
		} catch (BeanDefinitionNotFoundException e) {
			logger.warn("bean definition not found {}", beanName);
		} catch (BeanInstanceNotFoundException e) {
			logger.warn("bean instance not found {}", beanName);
		}
		return null;
	}

	@Override
	public <T> Map<String, T> getBeans(Class<T> beanType) {
		Map<String, T> result = new HashMap<String, T>();
		try {
			Set<BeanDefinition> mapping = this.beanDefinitionRegistry.getBeanDefinitions(beanType);
			if (null != mapping && mapping.size() > 0) {
				for (BeanDefinition bean : mapping) {
					T beanInstance = GenericUtils.parse(bean.getBeanInstance());
					result.put(bean.getBeanName(), beanInstance);
				}
			}
		} catch (BeanDefinitionNotFoundException e) {
			logger.warn("bean definition not found {}", beanType);
		} catch (BeanInstanceNotFoundException e) {
			logger.warn("bean instance not found {}", beanType);
		} catch (Exception e) {
			logger.warn(e.getMessage());
		}
		return result;
	}

	@Override
	protected void doStart() throws Exception {
		long timeStart = System.currentTimeMillis();
		this.resolveBeanDefinitionMappings();
		this.beanDefinitionRegistry.attachBeanDefinition(this);
		this.beanDefinitionRegistry.start();
		long timeClose = System.currentTimeMillis();
		logger.info("Application Context Start Success [{}ms]", timeClose - timeStart);
	}

	@Override
	protected void doClose() throws Exception {
		long timeStart = System.currentTimeMillis();
		this.beanDefinitionRegistry.close();
		long timeClose = System.currentTimeMillis();
		logger.info("Application Context Close Success [{}ms]", timeClose - timeStart);
	}

	@Override
	public BeanDefinitionRegistry getBeanDefinitionRegistry() {
		return this.beanDefinitionRegistry;
	}

	@Override
	protected ApplicationContext getActivity() {
		return this;
	}

	protected abstract BeanDefinitionRegistry createBeanDefinitionRegistry();

	protected abstract void resolveBeanDefinitionMappings();

	protected void resolveArguments(String[] args) {
		if (null != args && args.length > 0) {
			for (String arg : args) {
				if (StringUtils.isBlank(arg)) {
					continue;
				}
				if (arg.indexOf("=") == -1) {
					continue;
				}
				if (!arg.startsWith("-D")) {
					continue;
				}
				if (arg.trim().length() <= 3) {
					continue;
				}
				String[] conf = arg.trim().substring(2).split("=");
				String key = conf[0].trim();
				String val = conf[1].trim();
				logger.debug("option config success [{}={}]", key, val);
			}
		}
	}

}